<?php
// +----------------------------------------------------------------------
// | HkCms 内容模型
// +----------------------------------------------------------------------
// | Copyright (c) 2012-2019 http://www.hkcms.cn, All rights reserved.
// +----------------------------------------------------------------------
// | Author: 广州恒企教育科技有限公司 <admin@hkcms.cn>
// +----------------------------------------------------------------------

namespace app\content\model;

use common\model\Model as CommModel;
use think\Db;

class Model extends CommModel
{

    private $libPath = ''; //当前模块路径

    public $error = '';

    const mainTableSql = 'data/sql/hkcms_zhubiao.sql'; //模型主表SQL模板文件
    const sideTablesSql = 'data/sql/hkcms_zhubiao_data.sql'; //模型副表SQL模板文件
    const modelTablesInsert = 'data/sql/hkcms_insert.sql'; //可用默认模型字段
    const membershipModelSql = 'data/sql/hkcms_member.sql'; //会员模型

    //初始化
    public function initialize() {

        parent::initialize();
        $this->libPath = APP_PATH . 'content/';
    }

    /**
     * 检查需要创建的表名是否为系统保留名称
     * @param type $tablename 表名，不带表前缀
     * @return boolean 存在返回false，不存在返回true
     */
    public function checkTablename($tablename) {
        if (!$tablename) {
            return false;
        }
        //检查是否在保留内
        if (in_array($tablename, array("member_group", "member_content"))) {
            return false;
        }
        //检查该表名是否存在
        if ($this->table_exists($tablename)) {
            return false;
        }

        return true;
    }

    //检查SQL文件是否存在！
    public function checkTablesql() {
        //检查主表结构sql文件是否存在
        if (!is_file($this->libPath . self::mainTableSql)) {
            return false;
        }
        if (!is_file($this->libPath . self::sideTablesSql)) {
            return false;
        }
        if (!is_file($this->libPath . self::modelTablesInsert)) {
            return false;
        }
        if (!is_file($this->libPath . self::membershipModelSql)) {
            return false;
        }
        return true;
    }

    /**
     * 创建会员模型
     * @param type $tableName 模型主表名称（不包含表前缀）
     * @param type $modelId 所属模型id
     * @return boolean
     */
    public function AddModelMember($tableName, $modelId) {
        if (empty($tableName)) {
            return false;
        }
        //表前缀
        $dbPrefix = config("databases.prefix");
        //读取会员模型SQL模板
        $membershipModelSql = file_get_contents($this->libPath . self::membershipModelSql);
        //表前缀，表名，模型id替换
        $sqlSplit = str_replace(array('@hkcms@', '@zhubiao@', '@modelid@'), array($dbPrefix, $tableName, $modelId), $membershipModelSql);
        return $this->sql_execute($sqlSplit);
    }

    /**
     * 创建模型
     * @param type $data 提交数据
     * @return boolean
     */
    public function addModel($data) 
    {
        if (empty($data)) {

            return false;
        }

        //数据验证
        $model_v = new \app\content\validate\Model;

        $result = $model_v->check($data);

        if (!$result) {

            $this->error = $model_v->getError();

            return false;
        }

        if ( !$this->checkTablesql() ) {
            
            $this->error = '创建模型所需要的SQL文件丢失，创建失败！';

            return false;
        }

        if ( !$this->checkTablename($data['tablename'] ) ) {

            // 该表名是系统保留或者已经存在，不允许创建！
            $this->error = '该表名是系统保留或者已经存在，不允许创建！';

            return false;
        }

        //强制表名为小写
        $data['tablename'] = strtolower($data['tablename']);

        $data['addtime'] = time();

        //添加模型记录
        $this->insert($data);

        $modelid = $this->getLastInsID();

        if ($modelid) {

            //创建数据表
            if ($this->createModel($data['tablename'], $modelid)) {

                hk_cache("Model", NULL);

                return $modelid;
            } else {

                //表创建失败
                $this->where("modelid",$modelid)->delete();

                $this->error = '数据表创建失败！';

                return false;
            }

        } else {

            return false;
        }
    }

    /**
     * 编辑模型
     * @param type $data 提交数据
     * @return boolean
     */
    public function editModel($data, $modelid = 0) {

        if (empty($data)) {
            return false;
        }
        //模型ID
        $modelid = $modelid ? $modelid : (int) $data['modelid'];
        if (!$modelid) {
            $this->error = '模型ID不能为空！';
            return false;
        }
        //查询模型数据
        $info = $this->where("modelid",$modelid)->find();

        if (empty($info)) {
            $this->error = '该模型不存在！';
            return false;
        }

        //数据验证
        $model_v = new \app\content\validate\Model;

        $result = $model_v->scene('edit')->check($data);

        if (!$result) {

            $this->error = $model_v->getError();

            return false;
        }

        // 判断模型名称是否重复
        if ( $info['name'] != $data['name'] ) {

            $check = $this->where("name",$data['name'])->find();

            if ( $check ) {

                $this->error = '模型名已经存在';

                return false;
            }   
        }

        // 判断模型名称是否重复
        if ( $info['tablename'] != $data['tablename'] ) {

            $check = $this->where("tablename",$data['tablename'])->find();

            if ( $check ) {

                $this->error = '该表名已经存在！';

                return false;
            }   
        }

        $data['modelid'] = $modelid;

        if ($data) {
            //强制表名为小写
            $data['tablename'] = strtolower($data['tablename']);
            //是否更改表名
            if ($info['tablename'] != $data['tablename'] && !empty($data['tablename'])) {
                //检查新表名是否存在
                if ($this->table_exists($data['tablename']) || $this->table_exists($data['tablename'] . '_data')) {
                    $this->error = '该表名已经存在！';
                    return false;
                }
                if (false !== $this->where("modelid",$modelid)->update($data)) {
                    //表前缀
                    $dbPrefix = config("database.prefix");
                    //表名更改
                    if (!$this->sql_execute("RENAME TABLE  `{$dbPrefix}{$info['tablename']}` TO  `{$dbPrefix}{$data['tablename']}` ;")) {
                        $this->error = '数据库修改表名失败！';
                        return false;
                    }
                    //修改副表
                    if (!$this->sql_execute("RENAME TABLE  `{$dbPrefix}{$info['tablename']}_data` TO  `{$dbPrefix}{$data['tablename']}_data` ;")) {
                        //主表已经修改，进行回滚
                        $this->sql_execute("RENAME TABLE  `{$dbPrefix}{$data['tablename']}` TO  `{$dbPrefix}{$info['tablename']}` ;");
                        $this->error = '数据库修改副表表名失败！';
                        return false;
                    }
                    //更新缓存
                    hk_cache("Model", NULL);
                    return true;
                } else {
                    $this->error = '模型更新失败！';
                    return false;
                }
            } else {
                if (false !== $this->where("modelid",$modelid)->update($data)) {
                    return true;
                } else {
                    $this->error = '模型更新失败！';
                    return false;
                }
            }
        } else {
            return false;
        }
    }

    /**
     * 创建内容模型
     * @param type $tableName 模型主表名称（不包含表前缀）
     * @param type $modelId 模型id
     * @return boolean
     */
    protected function createModel($tableName, $modelId) 
    {
        if (empty($tableName) || $modelId < 1) {
            return false;
        }

        $Db_config = config('database');

        // 表前缀
        $dbPrefix = $Db_config['prefix'];

        //读取模型主表SQL模板
        $mainTableSqll = file_get_contents($this->libPath . self::mainTableSql);
        //副表
        $sideTablesSql = file_get_contents($this->libPath . self::sideTablesSql);
        //字段数据
        $modelTablesInsert = file_get_contents($this->libPath . self::modelTablesInsert);
        //表前缀，表名，模型id替换
        $sqlSplit = str_replace(array('@hkcms@', '@zhubiao@', '@modelid@'), array($dbPrefix, $tableName, $modelId), $mainTableSqll . "\n" . $sideTablesSql . "\n" . $modelTablesInsert);

        return $this->sql_execute($sqlSplit);
    }

    /**
     * 删除表
     * $table 不带表前缀
     */
    public function deleteTable($table) 
    {

        if ($this->table_exists($table)) {

            $this->drop_table($table);
        }

        return true;
    }

    /**
     * 根据模型ID删除模型
     * @param type $modelid 模型id
     * @return boolean
     */
    public function deleteModel($modelid) 
    {
        if (empty($modelid)) {

            return false;
        } 

        //这里可以根据缓存获取表名
        $modeldata = $this->where(array("modelid" => $modelid))->find();

        if (!$modeldata) {

            return false;
        }

        //表名
        $model_table = $modeldata['tablename'];

        //删除模型数据
        $this->where("modelid",$modelid)->delete();

        //更新缓存
        hk_cache("Model", NULL);

        //删除所有和这个模型相关的字段
        Db::name("ModelField")->where(array("modelid" => $modelid))->delete();

        //删除主表
        $this->deleteTable($model_table);

        if ((int) $modeldata['type'] == 0) {

            //删除副表
            $this->DeleteTable($model_table . "_data");
        }

        return true;
    }

    /**
     * 删除表
     * @param string $tablename 不带表前缀的表名
     * @return type
     */
    public function drop_table($tablename) 
    {
        $Db_config = config('database');

        $tablename = $Db_config['prefix'] . $tablename;

        return $this->query("DROP TABLE $tablename");
    }

    /**
     * 模型导入
     * @param type $data 数据
     * @param type $tablename 导入的模型表名
     * @param type $name 模型名称
     * @return int|boolean
     */
    public function import($data, $tablename = '', $name = '') {
                   
        if (empty($data)) {
            $this->error = '没有导入数据！';
            return false;
        }

        //解析
        $data = json_decode(base64_decode($data), true);

        if (empty($data)) {
            $this->error = '解析数据失败，无法进行导入！';
            return false;
        }
        //取得模型数据
        $model = $data['model'];
        if (empty($model)) {
            $this->error = '解析数据失败，无法进行导入！';
            return false;
        }
        config('TOKEN_ON', false);
        if ($name) {
            $model['name'] = $name;
        }
        if ($tablename) {
            $model['tablename'] = $tablename;
        }
        // dump($model);exit();
        //导入
        $modelid = $this->addModel($model);
        // echo "ok";exit();
        if ($modelid) {
            if (!empty($data['field'])) {
                foreach ($data['field'] as $value) {
                    $value['modelid'] = $modelid;
                    if ($value['setting']) {
                        $value['setting'] = unserialize($value['setting']);
                    }
                    $model = new \app\content\model\ModelField;
                    
                    if ($model->addField($value) == false) {
                        $value['setting'] = serialize($value['setting']);
                        $model->where(array('modelid' => $modelid, 'field' => $value['field'], 'name' => $value['name']))->update($value);
                    }
                    unset($model);
                }
            }
            return $modelid;
        } else {
            // dump($this->error);exit();
            return false;
        }
    }

    /**
     * 模型导出
     * @param type $modelid 模型ID
     * @return boolean
     */
    public function export($modelid) 
    {
        if (empty($modelid)) {

            $this->error = '请指定需要导出的模型！';

            return false;
        }

        //取得模型信息
        $info = $this->where(array('modelid' => $modelid, 'type' => 0))->find()->toArray();

        if (empty($info)) {

            $this->error = '该模型不存在，无法导出！';

            return false;
        }

        unset($info['modelid']);

        //数据
        $data = array();

        $data['model'] = $info;

        //取得对应模型字段
        $fieldList = Db::name('ModelField')->where('modelid',$modelid)->select();

        if (empty($fieldList)) {

            $fieldList = array();
        }

        // 去除fieldid，modelid字段内容
        foreach ($fieldList as $k => $v) {

            unset($fieldList[$k]['fieldid'], $fieldList[$k]['modelid']);
        }

        foreach ($fieldList as &$v) {

            unset($v['fieldid'], $v['modelid']);
        }

        $data['field'] = $fieldList;

        return base64_encode(json_encode($data));
        // return json_encode($data);
    }

    // //兼容方法...
    // public function delete_model($modelid) {
    //     return $this->deleteModel($modelid);
    // }

    // /**
    //  * 执行SQL
    //  * @param type $sqls SQL语句
    //  * @return boolean
    //  */
    // protected function sql_execute($sqls) {
    //     $sqls = $this->sql_split($sqls);
    //     if (is_array($sqls)) {
    //         foreach ($sqls as $sql) {
    //             if (trim($sql) != '') {
    //                 $this->execute($sql, true);
    //             }
    //         }
    //     } else {
    //         $this->execute($sqls, true);
    //     }
    //     return true;
    // }

    // /**
    //  * SQL语句预处理
    //  * @param type $sql
    //  * @return type
    //  */
    // public function sql_split($sql) {
    //     if (mysql_get_server_info() > '4.1' && C('DB_CHARSET')) {
    //         $sql = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=" . C('DB_CHARSET'), $sql);
    //     }
    //     if (C("DB_PREFIX") != "hkcms_") {
    //         $sql = str_replace("hkcms_", C("DB_PREFIX"), $sql);
    //     }
    //     $sql = str_replace("\r", "\n", $sql);
    //     $ret = array();
    //     $num = 0;
    //     $queriesarray = explode(";\n", trim($sql));
    //     unset($sql);
    //     foreach ($queriesarray as $query) {
    //         $ret[$num] = '';
    //         $queries = explode("\n", trim($query));
    //         $queries = array_filter($queries);
    //         foreach ($queries as $query) {
    //             $str1 = substr($query, 0, 1);
    //             if ($str1 != '#' && $str1 != '-')
    //                 $ret[$num] .= $query;
    //         }
    //         $num++;
    //     }
    //     return $ret;
    // }

    /**
     * 根据模型类型取得数据用于缓存
     * @param mixed $type
     * @return mixed
     */
    public function getModelAll($type = null)
    {
        $where = array('disabled' => 0);
        if (!is_null($type)) {
            $where['type'] = $type;
        }
        $data = $this->where($where)->select()->toArray();
        $Cache = array();
        foreach ($data as $v) {
            $Cache[$v['modelid']] = $v;
        }
        return $Cache;
    }

    /**
     * 生成模型缓存，以模型ID为下标的数组
     * @return mixed
     */
    public function model_cache()
    {
        $data = $this->getModelAll();
        cache('Model', $data);
        return $data;
    }

    /**
     * 检查表是否存在
     * $table 不带表前缀
     */
    public function table_exists($table) {
        $tables = $this->list_tables();
        $Db_config = config('database');
        // return in_array(C("DB_PREFIX") . $table, $tables) ? true : false;
        return in_array($Db_config['prefix'] . $table, $tables) ? true : false;
    }

    /**
     *  读取全部表名
     * @return type
     */
    public function list_tables() {
        $tables = array();
        $data = $this->query("SHOW TABLES");
        $Db_config = config('database');
        foreach ($data as $k => $v) {
            // $tables[] = $v['tables_in_' . C("DB_NAME")];
            $tables[] = $v['Tables_in_' . $Db_config['database']];
        }
        return $tables;
    }

    /**
     * 执行SQL
     * @param type $sqls SQL语句
     * @return boolean
     */
    protected function sql_execute($sqls) {
        $sqls = $this->sql_split($sqls);
        if (is_array($sqls)) {
            foreach ($sqls as $sql) {
                if (trim($sql) != '') {
                    $this->execute($sql);
                }
            }
        } else {
            $this->execute($sqls);
        }
        return true;
    }

    /**
     * SQL语句预处理
     * @param type $sql
     * @return type
     */
    public function sql_split($sql) 
    {
        $Db_config = config('database');

        $version = db()->query('select version() as ver;');

        if ($version[0]['ver'] > '4.1' && $Db_config['charset']) {   //mysql_get_server_info方法即将弃用

            $sql = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=" . $Db_config['charset'], $sql);
        }

        if ( $Db_config['prefix'] != "hkcms_") {

            $sql = str_replace("hkcms_", $Db_config['prefix'], $sql);
        }

        $sql = str_replace("\r", "\n", $sql);
        $ret = array();
        $num = 0;
        $queriesarray = explode(";\n", trim($sql));
        unset($sql);
        foreach ($queriesarray as $query) {
            $ret[$num] = '';
            $queries = explode("\n", trim($query));
            $queries = array_filter($queries);
            foreach ($queries as $query) {
                $str1 = substr($query, 0, 1);
                if ($str1 != '#' && $str1 != '-')
                    $ret[$num] .= $query;
            }
            $num++;
        }
        return $ret;
    }

    /**
     * 返回最近一条错误信息
     * @return type
     */
    public function getError() {
        return $this->error;
    }

}